package main

import (
	"bytes"
	"compress/gzip"
	"context"
	"fmt"
	"go.manyji.com/lib/app"
	"go.manyji.com/lib/config"
	"go.manyji.com/lib/encoding/json"
	"go.manyji.com/lib/log"
	_ "go.manyji.com/lib/log/filelog"
	_ "go.manyji.com/lib/systemd/autostart"
	"go.manyji.com/lib/util/req"
	"net"
	"net/http"
	"reflect"
	"strconv"
	"strings"
	"time"
)

var (
	fPort = config.Int("server.port", 9936, "http port")
)

func main() {
	app.Start()
	regMod(&mod_api{})
	http.HandleFunc("/", handleHttp)
	sv := &http.Server{Addr: ":" + strconv.Itoa(*fPort)}
	sv.SetKeepAlivesEnabled(false)
	log.Err(sv.ListenAndServe())
}

type ContextHttp struct {
	w http.ResponseWriter
	r *http.Request
}

type http_context struct {
	Uid  int
	Api  string
	Args req.ReqArgs
	Ctx  context.Context
	Http *ContextHttp
}

type Mod struct {
	apis map[string]func(*http_context) interface{}
}

var mods = &Mod{}

func regMod(mod interface{}) {
	t := reflect.TypeOf(mod)
	v := reflect.ValueOf(mod)
	m := &Mod{apis: map[string]func(*http_context) interface{}{}}
	for i := 0; i < t.NumMethod(); i++ {
		mname := t.Method(i).Name
		if strings.HasPrefix(mname, "API_") {
			m.apis[mname[4:]] = v.Method(i).Interface().(func(*http_context) interface{})
		}
	}
	mods = m
}

func (this *http_context) logReturnData(t time.Time) {
	retData := this.Args.Get("_result")
	this.Args.Del("_result")
	log.Debug(map[string]interface{}{
		"uid":        this.Uid,
		"ApiName":    this.Api,
		"UrlData":    this.Args.String(),
		"RetData":    retData,
		"CreateTime": time.Now().Unix(),
		"ExecTime":   time.Now().Sub(t).String(),
	})
}

func handleHttp(w http.ResponseWriter, r *http.Request) {
	defer log.RecoverPanic()
	now := time.Now()
	w.Header().Set("Access-Control-Allow-Origin", "*")
	ss := strings.Split(r.URL.Path[1:], "/")
	api := ss[0]
	if api == "" {
		w.Write([]byte("{\"Code\":4001,\"ErrMsg\":\"invalid api post\"}"))
		return
	}
	r.ParseForm()
	fn, ok := mods.apis[api]
	if !ok {
		w.Write([]byte("{\"Code\":4001,\"ErrMsg\":\"APINotFound\"}"))
		return
	}
	context := &http_context{
		Args: req.ReqArgs{},
		Api:  api,
		Http: &ContextHttp{w, r},
	}
	ip, _, _ := net.SplitHostPort(r.RemoteAddr)
	context.Args.Add("_ip", ip)
	defer func() {
		context.logReturnData(now)
	}()
	if strings.HasPrefix(api, "Upload") {
		err := r.ParseMultipartForm(32 << 20)
		if err != nil {
			w.Write([]byte("{\"Code\":4000,\"ErrMsg\":\"parse form wrong\"}"))
			return
		}
		context.Args = req.ParsePostForm(r.MultipartForm.Value)
	} else {
		context.Args = req.ParsePostForm(r.PostForm)
	}
	rfn := fn(context)
	if rfn == nil {
		return
	}
	if err, ok := rfn.(error); ok {
		w.WriteHeader(http.StatusForbidden)
		w.Write([]byte(fmt.Sprintf("{\"Code\":100,\"ErrMsg\":\"%s\"}", err.Error())))
		return
	}
	var b []byte
	switch v := rfn.(type) {
	case string:
		b = []byte(fmt.Sprintf(`{"Code":200,"Data":%s}`, v))
	default:
		bb, _ := json.Marshal(v)
		b = []byte(fmt.Sprintf(`{"Code":200,"Data":%s}`, string(bb)))
	}
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	w.Header().Set("Content-Encoding", "gzip")
	buf := new(bytes.Buffer)
	gzw := gzip.NewWriter(buf)
	leng, err := gzw.Write(b)
	if err != nil || leng == 0 {
		return
	}
	err = gzw.Flush()
	if err != nil {
		return
	}
	err = gzw.Close()
	if err != nil {
		return
	}
	bt := buf.Bytes()
	w.Write(bt)
}
